#include "stdafx.h"
#include "GameLib.h"
#include "Math/MersenneTwister.h"
#include "Private/Math/MersenneTwisterPrivate.h"
#include "Base/Debug.h"

ErrorCode MersenneTwister_Construct( MersenneTwister thisPtr, UINT seed ) {
	ErrorCode result = ERROR_NONE;
	NULL_CHECK( thisPtr, "MersenneTwister_Construct()", result | ERROR_NULL_OBJECT );

	thisPtr->m_rSeed = 1U;
	// safe0 start
	thisPtr->m_rSeedSp = 0U;
	thisPtr->m_mti = CMATH_N + 1;
	memset( thisPtr->m_mt, 0, sizeof( thisPtr->m_mt ) * CMATH_N );
	// safe0 end
	MersenneTwister_SetSeed( thisPtr, seed );

	return result;
}

ErrorCode MersenneTwister_Destruct( MersenneTwister thisPtr ) {
	ErrorCode result = ERROR_NONE;
	NULL_CHECK( thisPtr, "MersenneTwister_Destruct()", result | ERROR_NULL_OBJECT );

	return result;
}

MersenneTwister MersenneTwister_Create( UINT seed ) {
	MersenneTwister thisObj = NULL;

	ErrorCode result = ERROR_NONE;

	thisObj = (MersenneTwister)malloc( sizeof( MersenneTwister_t ) );
	NULL_CHECK( thisObj, "MersenneTwister_Create()", NULL );
	result |= MersenneTwister_Construct( thisObj, seed );

	if ( IS_ERROR_( result ) ) {
		Error( "MersenneTwister_Create()", "Can not construct object.", __LINE__, __FILE__ );
		thisObj = MersenneTwister_Release( thisObj );
		DESTROY_CHECK( thisObj, "MersenneTwister_Create()", NULL );
	}

	return thisObj;
}

MersenneTwister MersenneTwister_Release( MersenneTwister thisPtr ) {
	ErrorCode result = ERROR_NONE;
	NULL_CHECK( thisPtr, "MersenneTwister_Release()", NULL );

	result |= MersenneTwister_Destruct( thisPtr );

	if ( IS_ERROR_( result ) ) {
		Warning( "MersenneTwister_Release()", "Can not release object correctly.", __LINE__, __FILE__ );
	}
	if ( IS_ERROR_WARNING( result ) ) {
		return thisPtr;
	}

	SAFE_FREE( thisPtr );
	return NULL;
}

void MersenneTwister_SetSeed( MersenneTwister thisPtr, UINT newSeed ) {
	thisPtr->m_mt[ 0 ] = ( newSeed & 0xffffffff );
	for ( thisPtr->m_mti = 1; thisPtr->m_mti < CMATH_N; ++(thisPtr->m_mti) ) {
		thisPtr->m_mt[ thisPtr->m_mti ] = ( ( 69069UL * thisPtr->m_mt[ thisPtr->m_mti - 1 ] ) & 0xffffffff );
	}
	thisPtr->m_rSeed = newSeed;
}

UINT MersenneTwister_GetUInt( MersenneTwister thisPtr, UINT lessThanVal ) {
	ULONG y;
	static ULONG mag01[ 2 ] = {
		0x0, 
		CMATH_MATRIX_A
	};
	
	if ( lessThanVal == 0U ) {
		return 0U;
	}

	/* mag01[x] = x * MATRIX_A  for x=0,1 */

	if ( ( thisPtr->m_mti ) >= CMATH_N ) { /* generate N words at one time */
		int kk;

		if ( thisPtr->m_mti == ( CMATH_N + 1 ) ) {	/* if sgenrand() has not been called, */
			MersenneTwister_SetSeed( thisPtr, 4357U );	/* a default initial seed is used   */
		}

		for ( kk = 0; kk < ( CMATH_N - CMATH_M ); ++kk ) {
			y = ( ( thisPtr->m_mt[ kk ] & CMATH_UPPER_MASK ) | ( thisPtr->m_mt[ kk + 1 ] & CMATH_LOWER_MASK ) );
			thisPtr->m_mt[ kk ] = ( thisPtr->m_mt[ kk + CMATH_M ] ^ ( y >> 1 ) ^ mag01[ y & 0x1 ] );
		}
		for ( ; kk < ( CMATH_N - 1 ); ++kk ) {
			y = ( ( thisPtr->m_mt[ kk ] & CMATH_UPPER_MASK ) | ( thisPtr->m_mt[ kk + 1 ] & CMATH_LOWER_MASK ) );
			thisPtr->m_mt[ kk ] = ( thisPtr->m_mt[ kk + ( CMATH_M - CMATH_N ) ] ^ ( y >> 1 ) ^ mag01[ y & 0x1 ] );
		}

		y = ( ( thisPtr->m_mt[ CMATH_N - 1 ] & CMATH_UPPER_MASK ) | ( thisPtr->m_mt[ 0 ] & CMATH_LOWER_MASK ) );
		thisPtr->m_mt[ CMATH_N - 1 ] = ( thisPtr->m_mt[ CMATH_M - 1 ] ^ ( y >> 1 ) ^ mag01[ y & 0x1 ] );

		thisPtr->m_mti = 0;
	}

	y = thisPtr->m_mt[ (thisPtr->m_mti)++ ];
	y ^= CMATH_TEMPERING_SHIFT_U( y );
	y ^= CMATH_TEMPERING_SHIFT_S( y ) & CMATH_TEMPERING_MASK_B;
	y ^= CMATH_TEMPERING_SHIFT_T( y ) & CMATH_TEMPERING_MASK_C;
	y ^= CMATH_TEMPERING_SHIFT_L( y );

	// ET - old engine added one to the result.
	// We almost NEVER wanted to use this function
	// like this.  So, removed the +1 to return a 
	// range from 0 to n (not including n).
	return ( y % lessThanVal );
}

float MersenneTwister_GetFloat( MersenneTwister thisPtr ) {
	float r = (float)( MersenneTwister_GetUInt( thisPtr, 0xffffffff ) );
	float divisor = (float)( 0xffffffff );
	return ( ( r / divisor ) + 0.5f );
}

UINT MersenneTwister_GetSeed( MersenneTwister thisPtr ) {
	return thisPtr->m_rSeed;
}

void MersenneTwister_Rondomize( MersenneTwister thisPtr ) {
	MersenneTwister_SetSeed( thisPtr, (UINT)( time( NULL ) ) );
}
